﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using viWiki.Classes;

namespace viWiki.Controllers
{
    class FormateController
    {
        private static readonly Regex WikiPageLinkRegex = new Regex(@"(\[\[.+?\]\])", RegexOptions.Compiled);
        private static readonly Regex TocRegex = new Regex(@"\{toc\}", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
        private static readonly Regex H1Regex = new Regex(@"(\<h1\>.+?\<\/h1\>)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase);
        private static readonly Regex H2Regex = new Regex(@"(\<h2\>.+?\<\/h2\>)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase);
        private static readonly Regex H3Regex = new Regex(@"(\<h3\>.+?\<\/h3\>)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase);
        
        public static string TOC(string raw)
        {
            StringBuilder sb = new StringBuilder(raw);
            List<HPosition> hPos = new List<HPosition>();
            Match match;
            StringBuilder dummy;
            hPos = DetectHeaders(sb.ToString());
            int count = 0;
            int end = 0;
            string h;

            count = 0;
            match = H3Regex.Match(sb.ToString());
            while (match.Success)
            {
                sb.Remove(match.Index, match.Length);
                h = match.Value.Substring(4, match.Value.Length - 9 - (match.Value.EndsWith("\n") ? 1 : 0));
                dummy = new StringBuilder(200);
                dummy.Append(@"<h3 class=""separator"">");
                dummy.Append(h);
                string id = BuildHAnchor(h, count.ToString());
                dummy.Append("<a id=\"" + id + "\"></a>");
                dummy.Append("</h3>");
                sb.Insert(match.Index, dummy.ToString());
                count++;
                match = H3Regex.Match(sb.ToString(), end);
            }
            count = 0;
            match = H2Regex.Match(sb.ToString());
            while (match.Success)
            {
                sb.Remove(match.Index, match.Length);
                h = match.Value.Substring(4, match.Value.Length - 9 - (match.Value.EndsWith("\n") ? 1 : 0));
                dummy = new StringBuilder(200);
                dummy.Append(@"<h2 class=""separator"">");
                dummy.Append(h);
                string id = BuildHAnchor(h, count.ToString());                
                dummy.Append("<a id=\"" + id + "\"></a>");                
                dummy.Append("</h2>");
                sb.Insert(match.Index, dummy.ToString());
                count++;
                match = H2Regex.Match(sb.ToString(), end);
            }
            count = 0;
            match = H1Regex.Match(sb.ToString());
            while (match.Success)
            {
                sb.Remove(match.Index, match.Length);
                h = match.Value.Substring(4, match.Value.Length - 9 - (match.Value.EndsWith("\n") ? 1 : 0));
                dummy = new StringBuilder(200);
                dummy.Append(@"<h1 class=""separator"">");
                dummy.Append(h);
                string id = BuildHAnchor(h, count.ToString());
                dummy.Append("<a id=\"" + id + "\"></a>");                
                dummy.Append("</h1>");
                sb.Insert(match.Index, dummy.ToString());
                count++;
                match = H1Regex.Match(sb.ToString(), end);
            }            

            string tocString = BuildToc(hPos);

            match = TocRegex.Match(sb.ToString());
            while (match.Success)
            {
                sb.Remove(match.Index, match.Length);
                sb.Insert(match.Index, tocString);
                match = TocRegex.Match(sb.ToString());                
            }
            return sb.ToString();
        }       

        public static List<HPosition> DetectHeaders(string text)
        {
            Match match;
            string h;
            int end = 0;            
            List<HPosition> hPos = new List<HPosition>();
            StringBuilder sb = new StringBuilder(text);            
            int count = 0;                        
            match = H3Regex.Match(sb.ToString());
            while (match.Success)
            {
                h = match.Value.Substring(4, match.Value.Length - 9 - (match.Value.EndsWith("\n") ? 1 : 0));
                bool found = false;
                for (int i = 0; i < hPos.Count; i++)
                {
                    if (match.Index == hPos[i].Index)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    hPos.Add(new HPosition(match.Index, h, 3, count));
                    count++;
                }
                end = match.Index + match.Length;
                match = H3Regex.Match(sb.ToString(), end);
            }
            count = 0;
            match = H2Regex.Match(sb.ToString());
            while (match.Success)
            {                
                h = match.Value.Substring(4, match.Value.Length - 9 - (match.Value.EndsWith("\n") ? 1 : 0));
                bool found = false;
                for (int i = 0; i < hPos.Count; i++)
                {
                    if (match.Index == hPos[i].Index)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    hPos.Add(new HPosition(match.Index, h, 2, count));
                    count++;
                }
                end = match.Index + match.Length;
                match = H2Regex.Match(sb.ToString(), end);
            }
            count = 0;
            match = H1Regex.Match(sb.ToString());
            while (match.Success)
            {
                h = match.Value.Substring(4, match.Value.Length - 9 - (match.Value.EndsWith("\n") ? 1 : 0));
                bool found = false;
                for (int i = 0; i < hPos.Count; i++)
                {
                    if (match.Index == hPos[i].Index)
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    hPos.Add(new HPosition(match.Index, h, 1, count));
                    count++;
                }
                end = match.Index + match.Length;
                match = H1Regex.Match(sb.ToString(), end);
            }
            return hPos;
        }
        
        private static string BuildToc(List<HPosition> hPos)
        {
            StringBuilder sb = new StringBuilder();

            hPos.Sort(new HPositionComparer());
            
            sb.Append(@"<table id=""TocContainerTable""><tr><td>");
            sb.Append(@"<div id=""TocContainer"">");
            sb.Append(@"<h2 class=""small"">");
            sb.Append("Table of Contents");
            sb.Append("</h2>");
            sb.Append(@"<div id=""Toc"">");
            //sb.Append("<p><br />");
            sb.Append("<p>");
            for (int i = 0; i < hPos.Count; i++)
            {                
                switch (hPos[i].Level)
                {
                    case 1:
                        break;
                    case 2:
                        sb.Append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
                        break;
                    case 3:
                        sb.Append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
                        break;                    
                }
                if (hPos[i].Level == 1) sb.Append("<b>");
                if (hPos[i].Level == 4) sb.Append("<small>");
                sb.Append(@"<a href=""#");
                sb.Append(BuildHAnchor(hPos[i].Text, hPos[i].ID.ToString()));
                sb.Append(@""">");
                sb.Append(hPos[i].Text);
                sb.Append("</a>");
                if (hPos[i].Level == 4) sb.Append("</small>");
                if (hPos[i].Level == 1) sb.Append("</b>");
                sb.Append("<br />");
            }
            sb.Append("</p>");
            sb.Append("</div>");

            sb.Append("</div>");
            sb.Append("</td></tr></table>");

            return sb.ToString();
        }

        public static string BuildHAnchor(string h)
        {            
            StringBuilder sb = new StringBuilder(h);
            sb.Replace(" ", "_");
            sb.Replace(".", "");
            sb.Replace(",", "");
            sb.Replace(";", "");
            sb.Replace("\"", "");
            sb.Replace("/", "");
            sb.Replace("\\", "");
            sb.Replace("'", "");
            sb.Replace("(", "");
            sb.Replace(")", "");
            sb.Replace("[", "");
            sb.Replace("]", "");
            sb.Replace("{", "");
            sb.Replace("}", "");
            sb.Replace("<", "");
            sb.Replace(">", "");
            sb.Replace("#", "");
            sb.Replace("\n", "");
            sb.Replace("?", "");
            sb.Replace("&", "");
            sb.Replace("0", "A");
            sb.Replace("1", "B");
            sb.Replace("2", "C");
            sb.Replace("3", "D");
            sb.Replace("4", "E");
            sb.Replace("5", "F");
            sb.Replace("6", "G");
            sb.Replace("7", "H");
            sb.Replace("8", "I");
            sb.Replace("9", "J");
            return sb.ToString();
        }

        public static string BuildHAnchor(string h, string uid)
        {
            return BuildHAnchor(h) + "_" + uid;
        }

        public static string RawStringToHtml(string raw)
        {
            StringBuilder sb = new StringBuilder(raw);            
            Match match;
            string tmp, pageName, pageId;
            match = WikiPageLinkRegex.Match(sb.ToString());
            while (match.Success)
            {
                tmp = "";
                if (match.Value.Equals("[[]]"))
                    continue;
                if (match.Value.StartsWith("[["))
                    tmp = match.Value.Substring(2, match.Length - 4).Trim();
                sb.Remove(match.Index, match.Length);
                
                pageName = "";
                pageId = "";
                if (tmp.IndexOf("|") != -1)
                {
                    string[] fields = tmp.Split('|');
                    if (fields.Length == 2)
                    {
                        pageName = fields[0];
                        pageId = fields[1];
                        int PageId = 0;
                        string anchor = "";
                        Int32.TryParse(pageId, out PageId);
                        WikiPage wikipage = WikiPageController.GetWikiPage(PageId);
                        if(wikipage == null)
                            anchor = "<a href=\"#\">" + pageName + "</a>";// link is broken wiki page does'nt exist
                        else
                            anchor = "<a href=\"" + wikipage.Url +  "\">" + pageName + "</a>";
                        sb.Insert(match.Index, anchor);

                    }
                }                
                match = WikiPageLinkRegex.Match(sb.ToString());
            }
            return sb.ToString();
        }

        public static List<int> BuildPageLinks(string raw)
        {
            StringBuilder sb = new StringBuilder(raw);            
            Match match;
            List<int> linkedWikiPages = new List<int>();
            string tmp, pageName, pageId;
            match = WikiPageLinkRegex.Match(sb.ToString());
            while (match.Success)
            {
                tmp = "";
                if (match.Value.Equals("[[]]"))
                    continue;
                if (match.Value.StartsWith("[["))
                    tmp = match.Value.Substring(2, match.Length - 4).Trim();
                pageName = "";
                pageId = "";
                if (tmp.IndexOf("|") != -1)
                {
                    string[] fields = tmp.Split('|');
                    if (fields.Length == 2)
                    {
                        pageName = fields[0];
                        pageId = fields[1];
                        int PageId = 0;
                        if (Int32.TryParse(pageId, out PageId))
                            linkedWikiPages.Add(PageId);                            
                    }
                }                
                match = match.NextMatch();
            }
            return linkedWikiPages;
        }
    }

    public class HPosition
    {
        private int index;
        private string text;
        private int level;
        private int id;
        public HPosition(int index, string text, int level, int id)
        {
            this.index = index;
            this.text = text;
            this.level = level;
            this.id = id;
        }
        public int Index
        {
            get { return index; }
            set { index = value; }
        }
        public string Text
        {
            get { return text; }
            set { text = value; }
        }
        public int Level
        {
            get { return level; }
            set { level = value; }
        }
        public int ID
        {
            get { return id; }
            set { id = value; }
        }
    }

    public class HPositionComparer : IComparer<HPosition>
    {
         public int Compare(HPosition x, HPosition y)
        {
            return x.Index.CompareTo(y.Index);
        }
    }
}
